import { E_TABLE, IP_TABLE, P_TABLE, P_TABLE_T, SBOX_TABLE } from "./config"
import { HexToBinary, binaryToHex, getBinary, getBuffer, permutation, to10, to16, to2 } from "./utils"

enum factoryMode {
  d = 'decrypt',
  e = 'encrypt'
}

/**
 * 加密
 * @param text 加密的明文 
 * @param keys 子密钥组
 * @returns 加密后，16进制字符串
 */
export function encrypter(text: string, keys: string[]) {

  // 明文转 Buffer
  const buffer = getBuffer(text)
  const uint8Array = sliceGroup(buffer)

  let result = '' // 密文
  for (let i = 0; i < uint8Array.length; i++) {
    const buffer = Buffer.concat([uint8Array[i]])
    const binary = HexToBinary(buffer)

    result += algorithmFactory(binary, keys)
  }

  return result
}

/**
 * 解密
 * @param ciphertext 密文
 * @param keys 子密钥组
 * @returns 解密后的内容
 */
export function decrypter(ciphertext: string, keys: string[]) {
  const strArray = sliceGroup(ciphertext)

  let vision = '' // 解密后的 16 进制数
  strArray.forEach(item => {
    const binary = getBinary(item)
    vision += algorithmFactory(binary, keys, factoryMode.d)
  })
  // 将 vision 转为 Buffer 然后输出 明文 字符
  const arr = []
  for (let i = 0; i < vision.length; i += 2) {
    const value = vision.substring(i, i + 2)
    arr.push(parseInt('0x' + value))
  }
  const uint8Arr = new Uint8Array(arr)
  const buffer = Buffer.concat([uint8Arr])

  return buffer.toString()
}


/**
 * Buffer 数据分组
 * @param data Buffer 数据
 * @param limit 
 */
function sliceGroup(data: Buffer): Uint8Array[];
/**
 * 十六进制字符串分组
 * @param data 十六进制字符串
 * @param limit 每组的数据个数
 * @returns 
 */
function sliceGroup(data: string): string[];
/**
 * 123
 * @param data Buffer 数据
 * @param limit 
 * @returns 
 */
function sliceGroup(data: Buffer | string,): Uint8Array[] | string[] {

  if (Buffer.isBuffer(data) === true) {
    const limit = 8 // Buffer 数据每一项存在2个16进制数，故拿取64b的数据只需拿 8项
    const result = []
    const newBuffer = Uint8Array.prototype.slice.call(data)

    for (let i = 0; i < newBuffer.length / 8; i++) {
      const item = newBuffer.slice(i * limit, i * limit + limit)
      result.push(item)
    }

    return result
  } else if (typeof data === 'string') {
    const limit = 16 // 16进制字符串，每一项是一个16进制数，故拿取64b,需要拿16项 
    let result: string[] = []

    for (let i = 0; i < data.length / limit; i++) {
      const item = data.substring(i * limit, i * limit + limit)
      result.push(item)
    }

    return result
  }

  return ['']

}




/**
 * 算法工厂
 * @param binary 二进制数
 * @param subKeys 子密钥组
 * @param mode 模式，加密 | 解密模式
 * @returns 
 */
function algorithmFactory(binary: string, subKeys: string[] = [], mode = factoryMode.e) {
  // IP 置换
  const ip = permutation(binary, IP_TABLE)

  const ipL = ip.substring(0, ip.length / 2)
  const ipR = ip.substring(ip.length / 2)

  const f_iteration = f_iteration_16(ipL, ipR, subKeys, mode)

  let finalResult = permutation(f_iteration, P_TABLE_T)
  finalResult = binaryToHex(finalResult)

  return finalResult
}

/**
 * 16 轮 f 函数迭代
 * @param L 
 * @param R 
 */
function f_iteration_16(L0: string, R0: string, subKeys: string[] = [], mode = factoryMode.e) {
  const arr = []

  for (let i = 0; i < 16; i++) {
    const k = mode === factoryMode.e ? subKeys[i] : subKeys[15 - i] // 这一次迭代 对应的子密钥
    let L = ''
    let R = ''
    if (i !== 0) {
      // 其他 次
      L = arr[i - 1].R;
      const f_result = f(arr[i - 1].R, k);
      R = XOR(arr[i - 1].L, f_result)

    } else {
      // 第一次
      L = R0;
      const f_result = f(R0, k);
      R = XOR(L0, f_result)

    }
    arr.push({
      L,
      R
    })
  }

  return arr[arr.length - 1].R + arr[arr.length - 1].L
}

/**
 * 亦或运算，相同为0，否则为1
 * @param ER
 * @param K 子密钥
 * @returns 
 */
function XOR(ER: string, K: string) {
  let result = ''
  for (let i = 0; i < ER.length; i++) {
    const r = ER.substring(i, i + 1)
    const k = K.substring(i, i + 1)
    result += r === k ? '0' : '1'
  }

  return result
}

// f 函数
function f(R: string, K: string) {
  // # 1 E 扩展
  const ER = permutation(R, E_TABLE)
  // # 2 异或 运算
  const xor = XOR(ER, K)
  // # 3 放入 s-box 处理
  const sbox = SBOX(xor)
  // # 4 对S盒的输出结果进行 P 转换
  const result = permutation(sbox, P_TABLE)
  return result
}

// SBOX 运算
function SBOX(b: string) {
  // 分组
  const group = []
  for (let i = 0; i < b.length / 6; i++) {
    const item = b.substring(i * 6, i * 6 + 6)
    group.push(item)
  }

  let s: string = ''
  group.forEach((item, index) => {
    const rowIndex = +(item.substring(0, 1)) * 2 + (+item.substring(item.length - 1))
    const colIndex = to10(item.substring(1, item.length - 1))

    const result = SBOX_TABLE[index][rowIndex][colIndex]

    s += to2(result)

  })
  return s
}



function combine(a: string, b: string): string;  //方法的重载，
/**
 * 数字乘机
 * @param a
 * @param b
 */
function combine(a: number, b: number): number;//方法的重载

function combine(a: number | string, b: number | string): number | string {
  if (typeof a === "number" && typeof b === "number") {
    return a * b;
  } else if (typeof a === "string" && typeof b === "string") {
    return a + b;
  }

  throw new Error("type different a and b ")

}

// combine()